package com.douqu.game.plantwar.data.redis;

import com.douqu.game.plantwar.core.commons.AppConstant;
import com.douqu.game.plantwar.core.commons.BaseEntity;
import com.douqu.game.plantwar.core.manager.FarmManager;
import com.douqu.game.plantwar.core.manager.RoleManager;
import com.douqu.game.plantwar.core.utils.BeanMapConvertUtils;
import com.douqu.game.plantwar.core.utils.LogUtils;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * redis 注入
 * @param <HK>
 * @param <V>
 */
@Configuration
@EnableCaching
public class RedisConfig<HK,V> extends CachingConfigurerSupport {

    private RedisTemplate<String,V> redisTemplate;
    private HashOperations<String,HK,V> hashOperations;
    private ListOperations<String,V> listOperations;
    private ZSetOperations<String,V> zSetOperations;
    private SetOperations<String,V> setOperations;
    private ValueOperations<String,V> valueOperations;

    @Bean
    public KeyGenerator keyGenerator(){
        return (target,method,params)->{
            StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getName());
            sb.append(method.getName());
            Arrays.stream(params).forEach(sb::append);
            return sb.toString();
        };
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory){
        RedisCacheManager redisCacheManager = RedisCacheManager.builder(connectionFactory).build();
        return redisCacheManager;
    }

    @Bean
    public RedisTemplate<String,V> redisTemplate(RedisConnectionFactory redisConnectionFactory){
        //注入redis模板 redisTemplete 并声明每种类型的序列化方式
        LogUtils.info("注入 redisTemplate ...");
        RedisTemplate<String,V> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Object.class));
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new JdkSerializationRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean
    public Integer initConfig(RedisTemplate<String,V> redisTemplate){
        LogUtils.info("注入 redisService");
        this.redisTemplate = redisTemplate;
        this.hashOperations = redisTemplate.opsForHash();
        this.listOperations = redisTemplate.opsForList();
        this.zSetOperations = redisTemplate.opsForZSet();
        this.setOperations = redisTemplate.opsForSet();
        this.valueOperations = redisTemplate.opsForValue();
        return 1;
    }

    public void hashPut(String key, HK hashKey, V value) {
        hashOperations.put(key, hashKey, value);
    }

    public Map<HK, V> hashFindAll(String key) {
        return hashOperations.entries(key);
    }

    public V hashGet(String key, HK hashKey) {
        return hashOperations.get(key, hashKey);
    }

    public void hashRemove(String key, HK hashKey) {
        hashOperations.delete(key, hashKey);
    }

    public Long listPush(String key, V value) {
        return listOperations.rightPush(key, value);
    }

    public Long listUnshift(String key, V value) {
        return listOperations.leftPush(key, value);
    }

    public List<V> listFindAll(String key) {
        if (!redisTemplate.hasKey(key)) {
            return null;
        }
        return listOperations.range(key, 0, listOperations.size(key));
    }

    public V listLPop(String key) {
        return listOperations.leftPop(key);
    }

    public void setValue(Object key, V value) {
        if (value instanceof BaseEntity)
            ((BaseEntity) value).setUpdate(true);
        String realKey = AppConstant.getKey(key,value.getClass());
        valueOperations.set(realKey, value);
    }

    public void setValue(String key, V value, long timeout) {
        if (value instanceof BaseEntity)
            ((BaseEntity) value).setUpdate(true);
        ValueOperations<String, V> vo = redisTemplate.opsForValue();
        vo.set(key, value, timeout, TimeUnit.MILLISECONDS);
    }

    public V getValue(Object key,Class<V> type) {
        String realKey = AppConstant.getKey(key,type);
        return BeanMapConvertUtils.toBean(type, (Map<String,Object>)valueOperations.get(realKey));
    }

    public V getValueByRealKey(Object realKey,Class<V> type){
        return BeanMapConvertUtils.toBean(type, (Map<String,Object>)valueOperations.get(realKey));
    }

    public void remove(String key) {
        redisTemplate.delete(key);
    }

    public boolean expire(String key, long timeout, TimeUnit timeUnit) {
        return redisTemplate.expire(key, timeout, timeUnit);
    }

    public<V> Map<String,V> getAll(){
        Set<String> keys = redisTemplate.keys("*");
        Map<String,V> map = new ConcurrentHashMap<>();
        keys.forEach((key)->{

            Class c = null;
            if (key.indexOf(RoleManager.class.getSimpleName()) >= 0){
                c = RoleManager.class;
            }
            else if (key.indexOf(FarmManager.class.getSimpleName()) >= 0){
                c = FarmManager.class;
            }
            if (c != null){
                V v = (V) getValueByRealKey(key,c);
                map.put(key, v);
            }
        });
        return map;
    }
}
